package com.ssc.manager.trading.service.impl;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.ssc.manager.admin.dao.IBetDao;
import com.ssc.manager.admin.dao.IContractDao;
import com.ssc.manager.admin.dao.IUserAssetDao;
import com.ssc.manager.admin.entity.db.Bet;
import com.ssc.manager.admin.entity.db.Contract;
import com.ssc.manager.admin.entity.db.Play;
import com.ssc.manager.admin.entity.db.UserAccount;
import com.ssc.manager.admin.service.IUserAccountService;
import com.ssc.manager.common.cache.Cache;
import com.ssc.manager.common.constant.SystemConstant;
import com.ssc.manager.common.system.SystemData;
import com.ssc.manager.common.utils.BetiingUtils;
import com.ssc.manager.common.utils.PlayUtils;
import com.ssc.manager.common.utils.StringUtils;
import com.ssc.manager.trading.core.BettingQueue;
import com.ssc.manager.trading.dto.BetDto;
import com.ssc.manager.trading.service.IBettingService;

@Service
public class BettingService implements IBettingService {
	private int i =0;
	protected Logger log = Logger.getLogger(BettingQueue.class);
	
	@Autowired
	private Cache cache;
	
	@Autowired
	private IBetDao betdao;
	
	@Autowired
	private IContractDao contractDao;
	
	@Autowired
	private IUserAccountService userAccountService;
	
	@Autowired
	private IUserAssetDao userAssetDao;
	
	

	/**
	 * 成交注单方法
	 */
	@Override
	public void betting(Bet bet) {
		if(!SystemConstant.BET_END.equals(bet.getStatus())){
			boolean isClinch = true;
			while (isClinch) {
				List<Bet> bets = findClinchBets(bet);
				if(null != bets) {
					if(null != bets && 0 != bets.size()){
						while (isClinch && bets.size()>0) {
							Bet betTrigger = bets.get(0);
							//如果用户id一样。将取消自己的单 同等金额
							//bobo1 自我成交处理 不处理
							//if()
							clinchBet(bet, betTrigger);
							if(SystemConstant.BET_END.equals(betTrigger.getStatus())){
								SystemData.rmUserBet(betTrigger);
								bets.remove(0);
								cache.leftPush(SystemData.findBetListKey(String.valueOf(bet.getUserId())), SystemData.findUserBet(betTrigger.getUserId()));
							}
							if(SystemConstant.BET_END.equals(bet.getStatus())){
								isClinch = false;
							}
						}
						if(SystemConstant.BET_END.equals(bet.getStatus())){
							SystemData.removeBetByBet(bet);
							SystemData.rmUserBet(bet);
						}
					}
				}
			}
		}
		cache.leftPush(SystemData.findBetListKey(String.valueOf(bet.getUserId())), SystemData.findUserBet(bet.getUserId()));
		if(0!=bet.getCoeMoney()){
			showData(bet,bet.getCoeMoney(),SystemConstant.BET_SUM_CUT);
		}
	}
	
	
	
	/**
	 * 
	* 方法名称: betsQueue
	* 描述:多注单 
	* 参数：@param bets
	* 参数：@return 
	* @throws
	 */
	/*public String betsQueue(String bets) {
		List<BetDto> betlist = null;
		List<BetDto> betlistcroe = new ArrayList<BetDto>();
		try {
			betlist = JSON.parseArray(bets, BetDto.class);
		} catch (Exception e) {
			log.error("注单转换出错！！", e);
		}
		if(null == betlist){
			return IceConstant.BETDATANULL;
		}
		if(0 == betlist.size()){
			return IceConstant.BETDATANULL;
		}
		
		for(BetDto bet : betlist){
			String playkey = BetiingUtils.findPlaykey(bet.getGameCode(), bet.getPlayCode());
			BettingQueue betting = SystemData.findAddBettingQueue(playkey);
			
			betting.addQueue(bet);
		}
		return IceConstant.BETSUCCESS;
	}*/
	
	
	@Override
	public String betQueue(String betstr) {
		BetDto betDto = null;
		try {
			betDto = JSON.parseObject(betstr, BetDto.class);
		} catch (Exception e) {
			log.error("注单转换出错！！", e);
		}
		String state = filterBet(betDto);
		if(!SystemConstant.BET_SUCCESS.equals(state)){
			return state;
		}
		Bet bet = BetiingUtils.betConvertByDto(betDto);
		
		if(!haveMoney(bet)) {
			return SystemConstant.BET_NOT_MONEY;
		}
		bet = saveBet(bet);
		Play play = SystemData.findPlay(String.valueOf(bet.getPlayId()));
		if(null == play){
			return SystemConstant.BET_NO_PLAY;
		}
		BettingQueue betting = SystemData.findBettingQueue(play.getPlayCode());
		betting.addQueue(bet);
		return SystemConstant.BET_SUCCESS;
	}
	
	
	
	
//=======================private init===============================================
	
	
	
	/**
	 * 注单保存
	 * @param bet
	 * @return
	 */
	private Bet saveBet(Bet bet){
		//更新 用户金额 
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("userid", bet.getUserId());
		map.put("money",bet.getOrlMoney());
		userAssetDao.updateAssetMoney(map);
		
		betdao.insert(bet);
		SystemData.addBet(bet);
		SystemData.addUserBet(bet);
		showData(bet, bet.getOrlMoney(), SystemConstant.BET_SUM_ADD);
		cache.leftPush(SystemData.findBetListKey(String.valueOf(bet.getUserId())), SystemData.findUserBet(bet.getUserId()));
		return bet;
	}
	
	/**
	 * 过滤注单是否有用
	 * @param betDto
	 * @return
	 */
	private String filterBet(BetDto betDto){
		if(null == betDto){
			return SystemConstant.BET_DATA_NULL;
		}
		if(StringUtils.isEmpty(String.valueOf(betDto.getPlayid()))){
			return SystemConstant.BET_DATA_NULL;
		}
		if(!SystemData.isLivePlay(String.valueOf(betDto.getPlayid()))){
			return SystemConstant.BET_NO_PLAY;
		}
		UserAccount user = userAccountService.findUserAccount(String.valueOf(betDto.getUserid()));
		if(null == user){
			return SystemConstant.BET_USER_NOT;
		}
		return SystemConstant.BET_SUCCESS; 
	}
	
	
	/**
	 * 通过bet得取可成交的betlist
	 * @param bet
	 * @return
	 */
	private List<Bet> findClinchBets(final Bet bet){
		String reallyType = BetiingUtils.findReallyBetKey(bet.getItemCode(), bet.getTradeType());
		String contraryType = BetiingUtils.findContraryBetKey(bet.getReverseCode(), bet.getTradeType());
		
		Double reallyOdds,contraryOdds;
		do {
			reallyOdds =  SystemData.findGoodOddsKey(bet.getOdds(), reallyType, bet.getTradeType(), false);
		} while (null !=reallyOdds && !SystemData.isLiveBet(reallyOdds,reallyType));
		
		do {
			contraryOdds = SystemData.findGoodOddsKey(bet.getOdds(), contraryType, bet.getTradeType(), true);
		} while (null !=contraryOdds &&!SystemData.isLiveBet(contraryOdds,contraryType));
		
		
		if(null == reallyOdds && null ==contraryOdds){
			return null;
		}
		if(null ==reallyOdds){
			return SystemData.findBets(contraryOdds, contraryType);
		}
		if(null ==contraryOdds){
			return SystemData.findBets(reallyOdds,reallyType);
		}
		BigDecimal d = BigDecimal.valueOf(contraryOdds);
		Double cd =  SystemConstant.DFACTOR.divide(SystemConstant.DFACTOR.multiply(d),2,BigDecimal.ROUND_DOWN).doubleValue();
		if(SystemConstant.TRADEBUY.equals(bet.getTradeType())){
			if(reallyOdds>=cd){
				return SystemData.findBets(reallyOdds,reallyType);
			}else{
				return SystemData.findBets(contraryOdds, contraryType);
			}
		}else{
			if(reallyOdds<=cd){
				return SystemData.findBets(reallyOdds,reallyType);
			}else{
				return SystemData.findBets(contraryOdds, contraryType);
			}
		}
	}
	
	/**
	 * 成交两个注单
	 * @param source
	 * @param trigger
	 */
	private void clinchBet(Bet source,Bet trigger){
		double money = 0D;
		Double sOrlMoney = source.getOrlMoney()-source.getCoeMoney();
		Double tOrlMoney = trigger.getOrlMoney()-trigger.getCoeMoney();
		
		if(sOrlMoney.compareTo(tOrlMoney)==-1){
			money = source.getOrlMoney()-source.getCoeMoney();
		}else{
			money = trigger.getOrlMoney()-trigger.getCoeMoney();
		}
		
		//下面用到事务
		source.setCoeMoney(source.getCoeMoney()+money);
		if(source.getOrlMoney().compareTo(source.getCoeMoney()) == 0){
			source.setStatus(SystemConstant.BET_END);
		}
		
		trigger.setCoeMoney(trigger.getCoeMoney()+money);
		if(trigger.getOrlMoney().compareTo(trigger.getCoeMoney()) == 0){
			trigger.setStatus(SystemConstant.BET_END);
		}
		
		Contract contract = new Contract();
		contract.setClinchMoney(money);
		contract.setAbetId(source.getId());
		contract.setBbetId(trigger.getId());
		contract.setAodds(source.getOdds());
		contract.setBodds(trigger.getOdds());
		Double cd =  0D;
		if(source.getItemCode().equals(trigger.getItemCode())){
			cd = trigger.getOdds();
		}else{
			cd = SystemConstant.DFACTOR.divide(SystemConstant.DFACTOR.multiply(BigDecimal.valueOf(trigger.getOdds())),2,BigDecimal.ROUND_DOWN).doubleValue();
		}
		contract.setBoddsChange(cd);
		contract.setStatus(SystemConstant.CONTRACT_STATE_BEGING);
		
		contractDao.insert(contract);
		betdao.updateByPrimaryKey(source);
		betdao.updateByPrimaryKey(trigger);
		
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("userida", source.getUserId());
		map.put("useridb", trigger.getUserId());
		map.put("money",source.getUserId()==trigger.getUserId()?money*2:money);
		userAssetDao.updateFrozenMoney(map);
		
		showData(source, money, SystemConstant.BET_SUM_CUT);
		showData(trigger, money, SystemConstant.BET_SUM_CUT);
	}
	
	/**
	 * 展示数据处理 保存改变过汇总数据，再将数据保存到内存和缓存中。
	 * @param bet
	 */
	private void showData(Bet bet,Double money,String type){
		List<Double[]> list = SystemData.addSumData(bet,money,type);
		String playCode = SystemData.findCodeByBet(bet);
		String oddKey = PlayUtils.findPlayOddsKey(playCode);
		String itemkey = BetiingUtils.findSumItemKey(bet.getItemCode(),bet.getTradeType());
		long index = SystemData.findItemByIndex(playCode,itemkey);
		cache.listSet(oddKey, index,list);
	}
	
	/**
	 * 验证用户的余额
	 * @param bet
	 * @return
	 */
	private boolean haveMoney(Bet bet){
		Double userMoney = userAssetDao.findUserMoney(bet.getUserId());
		if(userMoney.compareTo(bet.getCoeMoney())<0)
			return false;
		return true;
	}

}
